﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;

namespace ESpace
{
    public class Comm
    {
        /// <summary> Редактирование значения </summary>
        /// <param name="Caption">Название в шапке</param>
        /// <param name="InitialValue">Строка с начальным значением</param>
        /// <param name="ConfirmClick">Фия-событие нажатия кнопки OK при редактировании. Для проверки и присвоения нового значения</param>
        public static void EditValueShowForm(string Caption, string InitialValue, ConfirmClickDelegate ConfirmClick)
        {
            EditValueShowForm(Caption, InitialValue, ConfirmClick, 250, false);
        }

        /// <summary> Редактирование значения </summary>
        /// <param name="Caption">Название в шапке</param>
        /// <param name="InitialValue">Строка с начальным значением</param>
        /// <param name="ConfirmClick">Фия-событие нажатия кнопки OK при редактировании. Для проверки и присвоения нового значения</param>
        /// <param name="Width">Ширина формы редактирования</param>
        public static void EditValueShowForm(string Caption, string InitialValue, ConfirmClickDelegate ConfirmClick, int Width)
        {
            EditValueShowForm(Caption, InitialValue, ConfirmClick, Width, false);
        }

        /// <summary> Редактирование значения </summary>
        /// <param name="Caption">Название в шапке</param>
        /// <param name="InitialValue">Строка с начальным значением</param>
        /// <param name="ConfirmClick">Фия-событие нажатия кнопки OK при редактировании. Для проверки и присвоения нового значения</param>
        /// <param name="Width">Ширина формы редактирования</param>
        public static void EditValueShowFormPosByCursor(string Caption, string InitialValue, ConfirmClickDelegate ConfirmClick, int Width)
        {
            EditValueShowForm(Caption, InitialValue, ConfirmClick, Width, true);
        }

        /// <summary> Редактирование значения </summary>
        /// <param name="Caption">Название в шапке</param>
        /// <param name="InitialValue">Строка с начальным значением</param>
        /// <param name="ConfirmClick">Фия-событие нажатия кнопки OK при редактировании. Для проверки и присвоения нового значения</param>
        public static void EditValueShowFormPosByCursor(string Caption, string InitialValue, ConfirmClickDelegate ConfirmClick)
        {
            EditValueShowForm(Caption, InitialValue, ConfirmClick, 250, true);
        }

        static void EditValueShowForm(string Caption, string InitialValue, ConfirmClickDelegate ConfirmClick, int Width, bool PosByCursor)
        {
            FormEditValue frm = new FormEditValue();
            frm.Text = Caption;
            if (Width < frm.MinimumSize.Width) Width = frm.MinimumSize.Width;
            frm.Width = Width;
            if (PosByCursor) frm.ShowDialogPosInCursor(InitialValue, ConfirmClick);
            else frm.ShowDialog(InitialValue, ConfirmClick);

            frm.Dispose();
        }

        /// <summary>MD5 из строки</summary>
        public static String GetMD5(String s)
        {
            return GetMD5(Encoding.UTF8.GetBytes(s));
        }

        /// <summary>MD5 из массива байт </summary>
        public static String GetMD5(byte[] buf)
        {
            byte[] hash = new MD5CryptoServiceProvider().ComputeHash(buf);
            return MD5String(hash);
        }

        /// <summary>MD5 из файла </summary>
        /// <param name="fPath">путь к файлу</param>
        /// <param name="AddLen">добавить ли к md5 размер файла</param>
        public static String GetMD5(String fPath, bool AddLen)
        {
            FileStream fstream = new FileStream(fPath, FileMode.Open);
            byte[] hash = new MD5CryptoServiceProvider().ComputeHash(fstream);
            fstream.Close();
            string ret = MD5String(hash);
            if (AddLen) ret += "_" + (new FileInfo(fPath).Length).ToString();
            return ret;
        }

        /// <summary>MD5 из первых байт файла </summary>
        /// <param name="fPath">путь к файлу</param>
        /// <param name="AddLen">добавить ли к md5 размер файла</param>
        /// <param name="MaxLen">количество первых байт для формирования MD5</param>
        public static String GetMD5(String fPath, bool AddLen, int MaxLen)
        {
            if ((new FileInfo(fPath).Length) <= MaxLen) return GetMD5(fPath, AddLen);

            FileStream fstream = new FileStream(fPath, FileMode.Open);
            byte[] buf = new byte[MaxLen];
            fstream.Read(buf, 0, MaxLen);
            fstream.Close();

            return GetMD5(buf) + (AddLen ? "_" + (new FileInfo(fPath).Length).ToString() : "");
        }

        /// <summary>MD5 из первых байт файла </summary>
        /// <param name="fPath">путь к файлу</param>
        /// <param name="AddLen">добавить ли к md5 размер файла</param>
        /// <param name="MaxLen">количество первых байт для формирования MD5</param>
        public static String GetMD5InCenterFile(String fPath, bool AddLen, int MaxLen)
        {
            long len = (new FileInfo(fPath)).Length;
            if (len <= MaxLen) return GetMD5(fPath, AddLen);
            FileStream fstream = new FileStream(fPath, FileMode.Open);

            long pos = (len - MaxLen) / 2;
            if ((pos + MaxLen) > len) pos = len - MaxLen;

            byte[] buf = new byte[MaxLen];
            fstream.Seek(pos, SeekOrigin.Begin);
            fstream.Read(buf, 0, MaxLen);
            fstream.Close();

            return GetMD5(buf) + (AddLen ? "_" + (new FileInfo(fPath).Length).ToString() : "");
        }

        // конвертирование в строку
        static string MD5String(byte[] hash)
        {
            StringBuilder sb = new StringBuilder(32);
            foreach (byte hex in hash)
                sb.Append(hex.ToString("X2"));
            return sb.ToString().ToUpper();
        }

        /// <summary>
        /// Добавление файла к существующему побайтно
        /// </summary>
        /// <param name="fPathPart">пусть к добавляемому файлу</param>
        /// <param name="fPathDest">пусть к файлу, в который добавляется</param>
        public static void AppendFile(string fPathPart, string fPathDest)
        {
            int lenBuf = 1024;
            FileStream fs1 = new FileStream(fPathPart, FileMode.Open); BinaryReader br1 = new BinaryReader(fs1);
            FileStream fs3 = new FileStream(fPathDest, FileMode.Append); BinaryWriter bw = new BinaryWriter(fs3);
            try
            {
                int pos = 0;
                bw.Seek(0, SeekOrigin.End);
                while (pos < fs1.Length)
                {
                    int len = lenBuf;
                    if (len > (fs1.Length - pos)) len = (int)(fs1.Length - pos);

                    byte[] buf = br1.ReadBytes(len);
                    bw.Write(buf);

                    pos += lenBuf;
                    if (pos >= fs1.Length) break;
                }
                pos = 0;

                bw.Flush();
            }
            catch { }
            finally
            {
                try
                {
                    fs1.Dispose();
                    fs3.Dispose();
                }
                catch { }
            }
        }

        /// <summary>
        /// Объединение файлов
        /// </summary>
        /// <param name="fPath1">пусть к файлу 1</param>
        /// <param name="fPath2">путь к файлу 2</param>
        /// <param name="fPathDest">путь к вновь создаваемому объединенному файлу</param>
        public static void UnionFiles(string fPath1, string fPath2, string fPathDest)
        {
            int lenBuf = 1024;
            FileStream fs1 = new FileStream(fPath1, FileMode.Open); BinaryReader br1 = new BinaryReader(fs1);
            FileStream fs2 = new FileStream(fPath2, FileMode.Open); BinaryReader br2 = new BinaryReader(fs2);
            FileStream fs3 = new FileStream(fPathDest, FileMode.Create); BinaryWriter bw = new BinaryWriter(fs3);
            try
            {
                int pos = 0;
                while (pos < fs1.Length)
                {
                    int len = lenBuf;
                    if (len > (fs1.Length - pos)) len = (int)(fs1.Length - pos);

                    byte[] buf = br1.ReadBytes(len);
                    bw.Write(buf);

                    pos += lenBuf;
                    if (pos >= fs1.Length) break;
                }
                pos = 0;
                while (pos < fs2.Length)
                {
                    int len = lenBuf;
                    if (len > (fs2.Length - pos)) len = (int)(fs2.Length - pos);

                    byte[] buf = br2.ReadBytes(len);
                    bw.Write(buf);

                    pos += lenBuf;
                }

                bw.Flush();
            }
            catch { }
            finally
            {
                try
                {
                    fs1.Dispose();
                    fs2.Dispose();
                    fs3.Dispose();
                }
                catch { }
            }
        }

        /// <summary>Получить имя файла, пригодное для сохранения (все спец-символы удаляются из строки)</summary>
        public static String GetValidFileName(String text)
        {
            return text.Replace("\\", "").Replace("/", "").Replace(":", "").Replace("?", "").Replace("\"", "").Replace("<", "").Replace(">", "").Replace("|", "");
        }

        /// <summary>Пригодно ли имя файла для сохранения. Вернет false, если в имени есть спец-символы</summary>
        public static bool FileNameIsValid(string fName)
        {
            string str = GetValidFileName(fName);
            return str == fName;
        }

        /// <summary>Возвращает имя файла по его пути</summary>
        public static string GetFileName(string fPath)
        {
            FileInfo fi = new FileInfo(fPath);
            int ind = fi.Name.LastIndexOf(".");
            if (ind == -1) return fi.Name;
            return fi.Name.Substring(0, ind);
        }

    }

    public class Cmpr
    {
        public static byte[] Compress(byte[] buf)
        {
            using (MemoryStream memoryStream = new MemoryStream())
            using (GZipStream zipStream = new GZipStream(memoryStream, CompressionMode.Compress, true))
            {
                zipStream.Write(buf, 0, buf.Length);
                zipStream.Close();
                return memoryStream.ToArray();
            }
        }

        public static byte[] Decompress(byte[] buf)
        {
            byte[] result = new byte[0];
            if ((buf != null) && (buf.Length != 0))
            {
                MemoryStream ms = new MemoryStream();
                ms.Write(buf, 0, buf.Length);
                ms.Position = 0;
                using (GZipStream unzipStream = new GZipStream(ms, CompressionMode.Decompress))
                {
                    byte[] unzipBuffer = new byte[512];
                    bool first = true;
                    while (true)
                    {
                        int bytesRead = unzipStream.Read(unzipBuffer, 0, unzipBuffer.Length);
                        if (bytesRead == 0 && !first)
                            break;

                        first = false;

                        Array.Resize(ref result, result.Length + bytesRead);
                        Array.Copy(unzipBuffer, 0, result, result.Length - bytesRead, bytesRead);
                    }
                }
            }
            return result;
        }

        // ужать текст
        public static byte[] CompressText(String text)
        {
            return Compress(Encoding.Unicode.GetBytes(text));
        }

        public static String DecompressText(byte[] buf)
        {
            return Encoding.Unicode.GetString(Decompress(buf));
        }
    }

    public class DiapItem<T> where T : IComparable
    {
        public T st;
        public T en;
        public DiapItem(T st, T en) { this.st = st; this.en = en; }
    }

    public class DiapCollection<T> where T : IComparable
    {
        List<DiapItem<T>> diapL = new List<DiapItem<T>>();

        public DiapItem<T> this[int i]
        {
            get { return diapL[i]; }
            set { diapL[i] = new DiapItem<T>(value.st, value.en); }
        }

        public void Add(T st, T en)
        {
            diapL.Add(new DiapItem<T>(st, en));
        }

        public int Count { get { return diapL.Count; } }

        public void Clear() { diapL.Clear(); }

        public DiapCollection<T> UnionListDiap()
        {
            if (diapL.Count == 0) return new DiapCollection<T>();

            DiapCollection<T> ret = new DiapCollection<T>();

            // клонируем в промежуточный список
            List<DiapItem<T>> dpL = new List<DiapItem<T>>();
            for (int i = 0; i < diapL.Count; i++)
                dpL.Add(new DiapItem<T>(diapL[i].st, diapL[i].en));

            ret.Add(dpL[0].st, dpL[0].en); // новый диапазон
            dpL.RemoveAt(0);
            while (dpL.Count > 0)
            {
                for (int n = 0; n < ret.Count; n++)
                {
                    if (dpL.Count == 0) break;

                    DiapItem<T> diap = ret[n];
                    bool bUnion = false;
                    for (int i = 0; i < dpL.Count; i++)
                    {

                        // если есть перекрещивание: объединяем, и удаляем не нужный более для проверки элемент

                        if (diap.st.CompareTo(dpL[i].st) <= 0 && diap.en.CompareTo(dpL[i].st) >= 0)
                        {
                            if (diap.en.CompareTo(dpL[i].en) < 0) diap.en = dpL[i].en;
                            dpL.RemoveAt(i); i--;
                            bUnion = true;
                        }
                        else if (diap.en.CompareTo(dpL[i].en) >= 0 && diap.st.CompareTo(dpL[i].en) <= 0)
                        {
                            if (diap.st.CompareTo(dpL[i].st) > 0) diap.st = dpL[i].st;
                            dpL.RemoveAt(i); i--;
                            bUnion = true;
                        }
                        else if (diap.st.CompareTo(dpL[i].st) > 0 && diap.en.CompareTo(dpL[i].en) < 0)
                        {
                            diap.st = dpL[i].st;
                            diap.en = dpL[i].en;
                            dpL.RemoveAt(i); i--;
                            bUnion = true;
                        }
                    }

                    if (!bUnion)
                    {
                        ret.Add(dpL[0].st, dpL[0].en);
                        dpL.RemoveAt(0);
                    }
                }
            }


            return ret;
        }
    }

}
